#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 12345

SSL_CTX *ctx;

SSL *Sync_init_ssl(int client_socket){
	SSL_library_init();
    SSL_CTX *ctx = SSL_CTX_new(SSLv23_client_method());
	if (ctx == NULL) {
        printf("Error creating SSL context\n");
        return 1;
    }

	SSL *ssl = SSL_new(ctx);

	SSL_set_fd(ssl, client_socket);

	return ssl;
	
}

int Creat_client_socket(){	
	int client_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (client_socket < 0) {
        printf("Error creating client socket\n");
        return 1;
    }
	return client_socket;
}

int Connet_to_server(int client_socket){    
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
    server_addr.sin_port = htons(SERVER_PORT);

	if (connect(client_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        printf("Error connecting to server\n");
        return -1;
    }
	return 0;
}

int Ssl_connect(int client_socket,SSL *ssl){
	if (SSL_connect(ssl) < 0) {
        printf("Error connecting SSL\n");
        close(client_socket);
        return 1;
    }
}

int Register(char *buffer, SSL *ssl){
	sprintf(buffer, "register", strlen("register"));
	SSL_write(ssl, buffer, strlen(buffer));
	memset(buffer, 0, sizeof(buffer));
	while(1){
	    printf("Make youself a prefer nickname:\n");
	    int ret = read(STDIN_FILENO, buffer, sizeof(buffer));
	    SSL_write(ssl, buffer, ret);
	    memset(buffer, 0, sizeof(buffer));
	    ret = SSL_read(ssl, buffer, sizeof(buffer));
	    if(ret < 0){
	    	printf("Error when read from server.");
	    }
	    ret = strcmp(buffer, "Name length out of max size!");
	    if(ret == 0){
		    printf("Name length out of max size, please try again!\n\n");
	    }else{
		printf("Name set okay.\n");
	    	break;
	    }
	    memset(buffer, 0, sizeof(buffer));
	}
	memset(buffer, 0, sizeof(buffer));
	while(1){
	    printf("Set your password:\n");
	    int ret = read(STDIN_FILENO, buffer, sizeof(buffer));
	    SSL_write(ssl, buffer, ret);
	    memset(buffer, 0, sizeof(buffer));
	    ret = SSL_read(ssl, buffer, sizeof(buffer));
	    if(ret < 0){
	    	printf("Error when read from server.");
	    }
	    ret = strcmp(buffer, "Password length out of max size!");
	    if(ret == 0){
			printf("Password length out of max size, please try again!\n\n");
		}else{
			printf("Password set okay.\n");
			break;
		}
		memset(buffer, 0, sizeof(buffer));
	}
	memset(buffer, 0, sizeof(buffer));
	SSL_read(ssl, buffer, sizeof(buffer));
	int ret = strcmp("Request OK!", buffer);
	if(ret == 0){
		printf("Registration OK! Please login!\n");
		return 0;
	}else{
		printf("Registration failed, Reason: ");
		write(STDOUT_FILENO, buffer, strlen(buffer));
		putchar('\n');
	}
}

int Operations(char *buffer, SSL *ssl){
	char log_flag;
	while(1){
		log_flag = getchar();
		if(log_flag == 'L'){
			sprintf(buffer, "login", strlen("login"));
			SSL_write(ssl, buffer, strlen(buffer));
			Login(buffer, ssl);
			Communicate(buffer, ssl);
		}else if(log_flag == 'R'){
			Register(buffer,ssl);
		}else{
			printf("Please type S or L.\n");
		}
		memset(buffer, 0, sizeof(buffer));
	}
	return 0;
}

int Login(char *buffer, SSL *ssl){
	memset(buffer, 0, sizeof(buffer));
    while(1){

        printf("Please enter your \033[0;36musername\033[0m:\n");
        read(STDIN_FILENO, buffer, sizeof(buffer));
        SSL_write(ssl, buffer, strlen(buffer));
        //int len = SSL_read(ssl, buffer, sizeof(buffer));
        //if(len < 0) printf("Error reading form SSL connection.\n");
        memset(buffer, 0, sizeof(buffer));

        printf("Please enter your \033[0;36mpassword\033[0m:\n");
        read(STDIN_FILENO, buffer, sizeof(buffer));
        SSL_write(ssl, buffer, strlen(buffer));
        //SSL_read(ssl, buffer, sizeof(buffer));

        memset(buffer, 0, sizeof(buffer));

	printf("=====================\n");

        int len = SSL_read(ssl, buffer, sizeof(buffer));
        if(len < 0){
        	printf("Unexpect disconnect.\n");
        }

        write(STDOUT_FILENO, buffer + 1, len - 1);
	printf("=====================\n");
        //printf("buffer[0] is %c\n",buffer[0]);
        if(buffer[0] == '0') break;
	memset(buffer, 0, sizeof(buffer));
    }
	memset(buffer, 0, sizeof(buffer));
	return 0;
}

int Communicate(char *buffer, SSL *ssl){
	while(1){
	write(STDOUT_FILENO,"Just say sth.:",sizeof("Just say sth.:"));
	read(STDIN_FILENO, buffer, sizeof(buffer));
	SSL_write(ssl, buffer, strlen(buffer));
    
 	   int len = SSL_read(ssl, buffer, sizeof(buffer)-1);
	   if (len < 0) {
	       printf("Error reading from SSL connection\n");
	   }
	   memset(buffer, 0, sizeof(buffer));
    }
}

void Fialnize(SSL *ssl, int client_socket, SSL_CTX *ctx){
	SSL_shutdown(ssl);
    close(client_socket);
    
    SSL_CTX_free(ctx);
}

void Print_welcome(){
	printf("Welcome!\nEnter \033[0;31mL/R\033[0m to login or register.\n");
}

int main(){
    
    int client_socket = Creat_client_socket();
       
    Connet_to_server(client_socket);

    SSL *ssl = Sync_init_ssl(client_socket);
        
    Ssl_connect(client_socket, ssl);
    	
	
    char buffer[1024];
    memset(buffer, 0, sizeof(buffer));

	Print_welcome();

	Operations(buffer, ssl);
        
    Fialnize(ssl, client_socket, ctx);
    
    return 0;
}
